﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Net.Sockets;
using System.Net;

namespace UDPKeyboard
{
    class Networking
    {
        int port = 5121; 
        BackgroundWorker workerUDP;
        UdpClient sockReceive;
        UdpClient sockSend;
        IPEndPoint ipHost;
        IPEndPoint ipLocal;
        IPEndPoint ipAny;
        byte[] data;

        Dictionary<ushort, bool> keyStates = new Dictionary<ushort, bool>();

        public delegate void KeyReceivedHandler(ushort scanCode, bool keyDown);
        public event KeyReceivedHandler KeyReceived;

        public Networking()
        {
            ipLocal = new IPEndPoint(IPAddress.Parse("127.0.0.1"), this.port);
            ipAny = new IPEndPoint(IPAddress.Any, this.port);
            sockReceive = new UdpClient(ipAny);
            sockSend = new UdpClient();

            workerUDP = new BackgroundWorker();
            workerUDP.WorkerReportsProgress = true;
            workerUDP.WorkerSupportsCancellation = true;
            workerUDP.DoWork += new DoWorkEventHandler(workerUDP_DoWork);
            workerUDP.ProgressChanged += new ProgressChangedEventHandler(workerUDP_ProgressChanged);
        }

        void workerUDP_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            bool keyDown = BitConverter.ToBoolean((byte[])e.UserState, 0);
            byte[] data = (byte[])e.UserState;
            ushort keyCode = BitConverter.ToUInt16(data, 1);
            if (keyCode != 0 ) 
                KeyReceived(keyCode, keyDown);
        }

        void workerUDP_DoWork(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                var data = sockReceive.Receive(ref ipAny);
                if (workerUDP.CancellationPending) return;
                workerUDP.ReportProgress(0, data);
            }
        }

        public void SendKey(bool keyDown, ushort key, bool suppress)
        {
            if (suppress && !keyStates.ContainsKey(key)) keyStates.Add(key, !keyDown);

            if (!suppress || keyStates[key] != keyDown)
            {
                byte[] ret = new byte[3];
                Buffer.BlockCopy(BitConverter.GetBytes(keyDown), 0, ret, 0, 1);
                Buffer.BlockCopy(BitConverter.GetBytes(key), 0, ret, 1, 2);
                data = ret;
                sockSend.Send(data, data.Length, (key == 0 ? ipLocal : ipHost));
            }
            if (suppress) keyStates[key] = keyDown;
        }

        public void Connect(string IP)
        {
            ipHost = new IPEndPoint(IPAddress.Parse(IP), port);
        }

        public void Disconnect()
        {
            ipHost = null;
        }

        public void StopHost()
        {
            workerUDP.CancelAsync();
            SendKey(false, 0, false);
        }

        public void Host()
        {
            workerUDP.RunWorkerAsync();
        }
    }
}
